home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Publishing / ImagePortfolio / Source / objectThreadPerform.m < prev    next >
Text File  |  1994-04-01  |  11KB  |  341 lines

  1. // -------------------------------------------------------------------------------------
  2. // objectThreadPerform.m
  3. // (see objectThreadPerform.h for usage information)
  4. // Martin D. Flynn, NeXT Computer, Inc.
  5. // -------------------------------------------------------------------------------------
  6. #import <stdio.h>
  7. #import <libc.h>
  8. #import <c.h>
  9. #import <dpsclient/dpsclient.h>
  10. #import <dpsclient/dpsNeXT.h>
  11. #import <servers/netname.h>
  12. #import <objc/zone.h>
  13. #import <mach/cthreads.h>
  14. #import <appkit/Application.h>
  15. #import <appkit/Panel.h>
  16. #import <appkit/Listener.h>
  17. #import <appkit/nextstd.h>
  18.  
  19. // -------------------------------------------------------------------------------------
  20. // misc defines
  21. #define    appPortFORMAT    "port_%s"
  22. #define    cpNil            (char*)nil
  23. #define    vpNil            (void*)nil
  24.  
  25. // -------------------------------------------------------------------------------------
  26. // main thread performance information structure
  27.  
  28. /* target/action perform information */
  29. #define    SIGNATURE        0xF121658F
  30. typedef struct targetAction_s {
  31.     long                    sig;            // structure signature
  32.     id                        target;
  33.     SEL                        action;
  34.     id                        arg[2];
  35.     int                        argCount;
  36.     any_t                    result;
  37.     mutex_t                    mutex;
  38.     condition_t                condition;
  39.     BOOL                    wait;
  40.     struct targetAction_s    *next;
  41. } targetAction_t;
  42.  
  43. /* mach port data structure */
  44. typedef struct {
  45.     msg_header_t            hdr;
  46.     msg_type_t                type;
  47.   void                        *data;
  48. } performMessage_t;
  49.  
  50. // -------------------------------------------------------------------------------------
  51. // global static variables
  52. static targetAction_t    *lastDp = (targetAction_t*)nil;        // last chained perform struct
  53. static mutex_t            performMutex = (mutex_t)nil;        // perform mutex
  54. static port_t            performPort = (port_t)nil;            // perform message port
  55. static cthread_t        mainThread = (cthread_t)nil;        // main thread
  56.  
  57. // -------------------------------------------------------------------------------------
  58. @implementation Object(ThreadPerform)
  59.  
  60. // -------------------------------------------------------------------------------------
  61. // target/action structure alloc/free
  62. // -------------------------------------------------------------------------------------
  63.  
  64. /* allocate target/action structure */
  65. static targetAction_t
  66. *allocActionStruct(id self, SEL aSel, id arg0, id arg1, int count, BOOL wait)
  67. {
  68.     targetAction_t    *dp = NXZoneMalloc([self zone], sizeof(targetAction_t));
  69.     dp->sig            = SIGNATURE;
  70.     dp->target        = self;
  71.     dp->action        = aSel;
  72.     dp->arg[0]        = arg0;
  73.     dp->arg[1]        = arg1;
  74.     dp->argCount    = count;
  75.     dp->wait        = wait;
  76.     dp->mutex        = mutex_alloc();
  77.     dp->condition    = condition_alloc();
  78.     dp->result        = (any_t)nil;
  79.     dp->next        = (targetAction_t*)nil;
  80.   return dp;
  81. }
  82.  
  83. /* free target/action structure */
  84. static targetAction_t *freeActionStruct(targetAction_t *dp)
  85. {
  86.     targetAction_t    *next;
  87.     mutex_lock(performMutex);
  88.     next = dp->next;
  89.     if (dp == lastDp) lastDp = (targetAction_t*)nil;
  90.     mutex_unlock(performMutex);
  91.     mutex_free(dp->mutex);
  92.     condition_free(dp->condition);
  93.     free(dp);
  94.     return next;
  95. }
  96.  
  97. // -------------------------------------------------------------------------------------
  98. // thread perform
  99. // -------------------------------------------------------------------------------------
  100.  
  101. /* perform with argCount specified */
  102. - perform:(SEL)selector with:arg1 with:arg2 argCount:(int)argCount
  103. {
  104.     switch (argCount) {
  105.         case 0: return [self perform:selector];                        break;
  106.         case 1: return [self perform:selector with:arg1];            break;
  107.         case 2: return [self perform:selector with:arg1 with:arg2];    break;
  108.     }
  109.     return (id)nil;
  110. }
  111.  
  112. /* perform target/action in structure */
  113. static id performActionStruct(targetAction_t *dp)
  114. {
  115.  
  116.     /* perform method */
  117.     mutex_lock(dp->mutex);
  118.     dp->result = [dp->target perform:dp->action with:dp->arg[0] with:dp->arg[1]
  119.                           argCount:dp->argCount];
  120.   
  121.     /* signal method completion */
  122.     condition_signal(dp->condition);
  123.     mutex_unlock(dp->mutex);
  124.   
  125.     /* return results */
  126.     return (id)dp->result;
  127. }
  128.  
  129. /* port message handler */
  130. static void _performProc(msg_header_t *msg, void *data)
  131. {
  132.     targetAction_t    *dp = ((performMessage_t*)msg)->data;
  133.     while (dp && (dp->sig == SIGNATURE)) {        // loop until no data, or wait
  134.         BOOL wait = dp->wait;                    // wait status save for later checking
  135.         performActionStruct(dp);                // execute action
  136.         if (wait) break;                        // break if waiting for return
  137.         dp = freeActionStruct(dp);                // free and get next structure
  138.     }
  139. }
  140.  
  141. /* port initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
  142. // - This function will be called automatically from forkPerform:...
  143. #define    doINIT   { if (mainThread == (cthread_t)nil) _doInit(); }
  144. static void _doInit()
  145. {
  146.     char    sName[256];
  147.   
  148.     /* return if already initialized */
  149.     if (mainThread) return;
  150.     mainThread = cthread_self();
  151.   
  152.     /* allocate perform mutex */
  153.     performMutex = mutex_alloc();
  154.  
  155.     /* allocate perform port (port name is made public) */
  156.     sprintf(sName, appPortFORMAT, [NXApp appName]);
  157.     if ((port_allocate(task_self(),&performPort) == KERN_SUCCESS) &&
  158.         (port_set_backlog(task_self(),performPort,PORT_BACKLOG_MAX) == KERN_SUCCESS) &&
  159.         (netname_check_in(name_server_port,sName,PORT_NULL,performPort) == NETNAME_SUCCESS)) {
  160.         DPSAddPort(performPort, _performProc, MSG_SIZE_MAX, vpNil, NX_MODALRESPTHRESHOLD);
  161.     } else {
  162.         NXLogError("objectThreadPerfrom: Unable to allocate port for thread support");
  163.         performPort = (port_t)nil;
  164.         exit(255);
  165.     }
  166.     
  167. }
  168.  
  169. /* explicit initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
  170. + initThreadSupport
  171. {
  172.     doINIT;
  173.     return self;
  174. }
  175.  
  176. /* return true if calling thread is main thread */
  177. + (BOOL)isMainThread { return (mainThread == cthread_self()); }
  178. - (BOOL)isMainThread { return (mainThread == cthread_self()); }
  179.  
  180. // -------------------------------------------------------------------------------------
  181. // port message perform support
  182. // multiple calls to 'mainThreadPerform:with:wait:' with wait:NO will be chained together
  183. // if the prior call has not completed execution.  This is done to reduce the load on
  184. // mach_port usage.
  185. // -------------------------------------------------------------------------------------
  186.  
  187. /* send perform message to port */
  188. static BOOL _sendPerformToPort(port_t portId, void *data)
  189. {
  190.     performMessage_t     pm;
  191.     msg_return_t        err;
  192.     msg_timeout_t        timeout = 45000;                    // 45 seconds 
  193.  
  194.     /* check for valid port */
  195.     if (!portId) return YES;
  196.   
  197.     /* set up the header */
  198.     pm.hdr.msg_simple            = TRUE;                    // data is inline
  199.     pm.hdr.msg_size                = sizeof(performMessage_t);
  200.     pm.hdr.msg_type                = MSG_TYPE_NORMAL;
  201.     pm.hdr.msg_remote_port         = portId;                // destination port 
  202.     pm.hdr.msg_local_port        = PORT_NULL;
  203.     pm.hdr.msg_id                = 0;                    // receiver message type
  204.  
  205.     /* set up the typeDescriptor */
  206.     pm.type.msg_type_name        = MSG_TYPE_CHAR;
  207.     pm.type.msg_type_size        = 8;                    // 8 bits / byte
  208.     pm.type.msg_type_inline        = TRUE;                    // data is inline
  209.     pm.type.msg_type_number        = sizeof(targetAction_t*);
  210.   
  211.     /* set up the data */
  212.     pm.type.msg_type_longform    = FALSE;
  213.     pm.type.msg_type_deallocate    = FALSE;                // do not deallocate
  214.     pm.data                        = data;                    // the data
  215.  
  216.     /* send message and return results */
  217.     err = msg_send((msg_header_t*)&pm, (msg_option_t)SEND_TIMEOUT, timeout);
  218.     return (err == SEND_SUCCESS)? NO: YES;
  219.   
  220. }
  221.  
  222. /* local mainThreadPerform method */
  223. static id _mainThreadPerform(port_t portId, targetAction_t *dp)
  224. {
  225.     any_t    result;
  226.   
  227.     /* check non-wait request */
  228.     if (!dp->wait) {
  229.         mutex_lock(performMutex);
  230.         mutex_lock(dp->mutex);
  231.         if (lastDp) lastDp->next = dp;
  232.         else _sendPerformToPort(portId, (void*)dp);
  233.         lastDp = dp;
  234.         mutex_unlock(dp->mutex);
  235.         mutex_unlock(performMutex);
  236.         return (id)nil;
  237.     }
  238.  
  239.     /* send message and wait for return */
  240.     mutex_lock(dp->mutex);
  241.     if (!_sendPerformToPort(portId, (void*)dp)) condition_wait(dp->condition, dp->mutex);
  242.     mutex_unlock(dp->mutex);
  243.     result = dp->result;
  244.   
  245.     /* free structure */
  246.     freeActionStruct(dp);
  247.  
  248.     return (id)result;
  249.   
  250. }
  251.  
  252. /* perform selector from main thread (no args) */
  253. - mainThreadPerform:(SEL)aSelector wait:(BOOL)waitForReturn
  254. {
  255.     targetAction_t    *dp;
  256.     doINIT; /* just in case: we better be the main thread if this is executed! */
  257.     if ([self isMainThread]) return [self perform:aSelector];
  258.     dp = allocActionStruct(self, aSelector, (id)nil, (id)nil, 0, waitForReturn);
  259.     return _mainThreadPerform(performPort, dp);
  260. }
  261.  
  262. /* perform selector from main thread (1 arg) */
  263. - mainThreadPerform:(SEL)aSelector with:anArg wait:(BOOL)waitForReturn
  264. {
  265.     targetAction_t    *dp;
  266.     doINIT; /* just in case: we better be the main thread if this is executed! */
  267.     if ([self isMainThread]) return [self perform:aSelector with:anArg];
  268.     dp = allocActionStruct(self, aSelector, anArg, (id)nil, 1, waitForReturn);
  269.     return _mainThreadPerform(performPort, dp);
  270. }
  271.  
  272. /* perform selector from main thread (2 args) */
  273. - mainThreadPerform:(SEL)aSelector with:anArg0 with:anArg1 wait:(BOOL)waitForReturn
  274. {
  275.     targetAction_t    *dp;
  276.     doINIT; /* just in case: we better be the main thread if this is executed! */
  277.     if ([self isMainThread]) return [self perform:aSelector with:anArg0 with:anArg1];
  278.     dp = allocActionStruct(self, aSelector, anArg0, anArg1, 2, waitForReturn);
  279.     return _mainThreadPerform(performPort, dp);
  280. }
  281.  
  282. // -------------------------------------------------------------------------------------
  283. // fork thread support
  284. // -------------------------------------------------------------------------------------
  285.  
  286. /* forked method router */
  287. static void threadRouter(targetAction_t *dp)
  288. {
  289.   
  290.     /* wait here until parent thread is ready */
  291.     mutex_lock(dp->mutex);
  292.     mutex_unlock(dp->mutex);
  293.   
  294.     /* execute thread */
  295.     performActionStruct(dp);                        // execute action
  296.     freeActionStruct(dp);                            // free structure
  297.  
  298.     /* terminate thread */
  299.     cthread_exit(0);                                // terminate thread
  300.   
  301. }
  302.  
  303. /* fork method thread */
  304. - (cthread_t)forkPerform:(SEL)aSelector with:arg0 with:arg1 argc:(int)cnt detach:(BOOL)detach
  305. {
  306.     cthread_t        cthread;
  307.     targetAction_t    *dp = allocActionStruct(self, aSelector, arg0, arg1, cnt, NO);
  308.   
  309.     /* initialize if necessary (for the first time, we better be the main thread!)*/
  310.     doINIT;
  311.   
  312.       /* fork thread */
  313.     mutex_lock(dp->mutex);
  314.     cthread = cthread_fork((cthread_fn_t)threadRouter, (any_t)dp);
  315.     if (detach) cthread_detach(cthread);
  316.     mutex_unlock(dp->mutex);
  317.     cthread_yield();        // allow thread to run
  318.   
  319.     return (detach)? (cthread_t)nil: cthread;        // handle may not be valid if detached
  320. }
  321.  
  322. /* fork method thread */
  323. - (cthread_t)forkPerform:(SEL)aSelector detach:(BOOL)detach
  324. {
  325.     return [self forkPerform:aSelector with:(id)nil with:(id)nil argc:0 detach:detach];
  326. }
  327.  
  328. /* fork method thread */
  329. - (cthread_t)forkPerform:(SEL)aSelector with:anArg detach:(BOOL)detach
  330. {
  331.     return [self forkPerform:aSelector with:anArg with:(id)nil argc:1 detach:detach];
  332. }
  333.  
  334. /* fork method thread */
  335. - (cthread_t)forkPerform:(SEL)aSelector with:anArg0 with:anArg1 detach:(BOOL)detach
  336. {
  337.     return [self forkPerform:aSelector with:anArg0 with:anArg1 argc:2 detach:detach];
  338. }
  339.  
  340. @end
  341.